/**
 * 
 */
package info.niwota.frames.net;

import java.io.Closeable;
import java.io.IOException;
import java.io.InputStream;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
import java.io.OutputStream;
import java.net.Socket;
import java.util.HashMap;

/**
 * @author qiangli
 * 
 */
public class TransportContext {
	private static TransportContext instance = new TransportContext();

	private static HashMap<String, TransportData> map = new HashMap<String, TransportData>();

	public static TransportContext getInstance() {
		return instance;
	}

	public TransportData getData(String key) {
		return key == null ? null : map.get(key);
	}

	public void removeData(String key) {
		map.remove(key);
	}

	public void setData(String key, TransportData data) {
		map.put(key, data);
	}

	public static void sendRequest(ObjectOutputStream out, TransportRequest req)
			throws IOException {
		out.writeObject(req.action);
		out.writeObject(req.data);
		out.flush();
	}

	public static TransportData readRequest(ObjectInputStream in)
			throws Exception {
		final String action = (String) in.readObject();
		final Object req = in.readObject();
		TransportData data = new TransportData();
		data.request = req;
		return data;
	}

	public static void readResponse(ObjectInputStream in, TransportResponse resp)
			throws IOException {
		try {
			final int rc = in.readInt();
			resp.code = rc;
			//
			resp.message = (String) in.readObject();
			if (rc == TransportResponse.OK) {
				resp.data = in.readObject();
			}
		} catch (IOException e) {
			throw e;
		} catch (ClassNotFoundException e) {
			throw new IOException(e.getMessage());
		}
	}

	public static void sendResponse(ObjectOutputStream out, String msg, Object data)
			throws IOException {
		out.writeInt(TransportResponse.OK);
		out.writeObject(msg);
		out.writeObject(data);
		out.flush();
	}

	public static void sendErrorResponse(ObjectOutputStream out, Object e)
			throws IOException {
		out.writeInt(TransportResponse.ERROR);
		out.writeObject(e);
		out.flush();
	}
	
	public static void handle(TransportRequest req, TransportResponse resp) throws IOException, ClassNotFoundException {
		final Socket c = new Socket(req.host, req.port);
		final OutputStream out = c.getOutputStream();
		//
		ObjectOutputStream oos = new ObjectOutputStream(out);
		
		TransportContext.sendRequest(oos, req);
		
		InputStream in = c.getInputStream();
		ObjectInputStream ois = new ObjectInputStream(in);
		
		TransportContext.readResponse(ois, resp);
		
		//
		close(in);
		close(out);
		
		close(c);
	}
	
	private static void close(Closeable c) {
		try {
			c.close();
		} catch (Exception e) {
			e.printStackTrace();
		}
	}
	
	private static void close(Socket c) {
		try {
			c.close();
		} catch (Exception e) {
			e.printStackTrace();
		}
	}
	
	public static TransportRequestHandler connect(TransportRequest req) throws IOException{
		TransportRequestHandler handler = new TransportRequestHandler();
		handler.init(req);
		
		return handler;
	}
}
